package com.github.bkhezry.mapdrawingtools.ui;
import android.content.Context;
import android.content.Intent;
import android.content.IntentSender;
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.drawable.Drawable;
import android.location.Location;
import android.os.Bundle;
import android.support.annotation.IdRes;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.support.design.widget.FloatingActionButton;
import android.support.v4.content.ContextCompat;
import android.support.v7.app.AppCompatDelegate;
import android.util.Log;
import android.view.View;
import android.widget.TextView;
import android.widget.Toast;
import com.github.bkhezry.mapdrawingtools.R;
import com.github.bkhezry.mapdrawingtools.model.DataModel;
import com.github.bkhezry.mapdrawingtools.model.DrawingOption;
import com.github.bkhezry.mapdrawingtools.utils.CalUtils;
import com.google.android.gms.common.ConnectionResult;
import com.google.android.gms.common.api.GoogleApiClient;
import com.google.android.gms.common.api.PendingResult;
import com.google.android.gms.common.api.ResultCallback;
import com.google.android.gms.common.api.Status;
import com.google.android.gms.location.LocationRequest;
import com.google.android.gms.location.LocationServices;
import com.google.android.gms.location.LocationSettingsRequest;
import com.google.android.gms.location.LocationSettingsResult;
import com.google.android.gms.location.LocationSettingsStatusCodes;
import com.google.android.gms.maps.CameraUpdateFactory;
import com.google.android.gms.maps.GoogleMap;
import com.google.android.gms.maps.OnMapReadyCallback;
import com.google.android.gms.maps.SupportMapFragment;
import com.google.android.gms.maps.model.BitmapDescriptor;
import com.google.android.gms.maps.model.BitmapDescriptorFactory;
import com.google.android.gms.maps.model.LatLng;
import com.google.android.gms.maps.model.Marker;
import com.google.android.gms.maps.model.MarkerOptions;
import com.google.android.gms.maps.model.Polygon;
import com.google.android.gms.maps.model.PolygonOptions;
import com.google.android.gms.maps.model.Polyline;
import com.google.android.gms.maps.model.PolylineOptions;
import java.util.ArrayList;
import java.util.List;
import java.util.Locale;
import pl.charmas.android.reactivelocation.ReactiveLocationProvider;
import rx.Observable;
import rx.Subscription;
import rx.functions.Action1;
import rx.subscriptions.CompositeSubscription;
public class MapsActivity extends BaseActivity implements OnMapReadyCallback, GoogleApiClient.ConnectionCallbacks,
GoogleApiClient.OnConnectionFailedListener {
public final static String MAP_OPTION = "map_option";
public final static int REQUEST_CHECK_SETTINGS = 0;
public static final String POINTS = "points";
private GoogleMap mMap;
private Location currentLocation;
private List<LatLng> points = new ArrayList<>();
private List<Marker> markerList = new ArrayList<>();
private Polygon polygon;
private Polyline polyline;
private ReactiveLocationProvider locationProvider;
private Observable<Location> lastKnownLocationObservable;
private Observable<Location> locationUpdatesObservable;
private Subscription lastKnownLocationSubscription;
private Subscription updatableLocationSubscription;
private Marker currentMarker;
private CompositeSubscription compositeSubscription;
private final static String TAG = "MapsActivity";
private boolean isGPSOn = false;
private GoogleApiClient mGoogleApiClient;
private DrawingOption drawingOption;
private View calLayout;
private TextView areaTextView;
private TextView lengthTextView;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_maps);
AppCompatDelegate.setCompatVectorFromResourcesEnabled(true);
drawingOption = getIntent().getParcelableExtra(MAP_OPTION);
SupportMapFragment mapFragment = (SupportMapFragment) getSupportFragmentManager()
.findFragmentById(R.id.map);
mapFragment.getMapAsync(this);
setUpFABs();
setUpCalculateLayout();
initRequestingLocation();
if (drawingOption.getRequestGPSEnabling())
requestActivatingGPS();
}
private void setUpFABs() {
final FloatingActionButton btnSatellite = (FloatingActionButton) findViewById(R.id.btnSatellite);
btnSatellite.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
mMap.setMapType(mMap.getMapType() == GoogleMap.MAP_TYPE_SATELLITE ? GoogleMap.MAP_TYPE_NORMAL : GoogleMap.MAP_TYPE_SATELLITE);
btnSatellite.setImageResource(mMap.getMapType() == GoogleMap.MAP_TYPE_SATELLITE ? R.drawable.ic_satellite_off : R.drawable.ic_satellite_on);
}
});
btnSatellite.setVisibility(drawingOption.getEnableSatelliteView() ? View.VISIBLE : View.GONE);
FloatingActionButton btnUndo = (FloatingActionButton) findViewById(R.id.btnUndo);
btnUndo.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
if (points.size() > 0) {
Marker marker = markerList.get(markerList.size() - 1);
marker.remove();
markerList.remove(marker);
points.remove(points.size() - 1);
if (points.size() > 0) {
if (drawingOption.getDrawingType() == DrawingOption.DrawingType.POLYGON) {
drawPolygon(points);
} else if (drawingOption.getDrawingType() == DrawingOption.DrawingType.POLYLINE) {
drawPolyline(points);
}
}
}
}
});
FloatingActionButton btnDone = (FloatingActionButton) findViewById(R.id.btnDone);
btnDone.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
returnCurrentPosition();
}
});
FloatingActionButton btnGPS = (FloatingActionButton) findViewById(R.id.btnGPS);
btnGPS.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
if (!isGPSOn) {
requestActivatingGPS();
} else {
if (compositeSubscription != null && locationUpdatesObservable != null) {
getLastKnowLocation();
}
}
}
});
}
private void setUpCalculateLayout() {
calLayout = findViewById(R.id.calculate_layout);
calLayout.setVisibility(drawingOption.getEnableCalculateLayout() ? View.VISIBLE : View.GONE);
areaTextView = (TextView) findViewById(R.id.areaTextView);
lengthTextView = (TextView) findViewById(R.id.lengthTextView);
}
private void initRequestingLocation() {
mGoogleApiClient = new GoogleApiClient.Builder(this)
.addApi(LocationServices.API)
.addConnectionCallbacks(this)
.addOnConnectionFailedListener(this).build();
mGoogleApiClient.connect();
compositeSubscription = new CompositeSubscription();
locationProvider = new ReactiveLocationProvider(getApplicationContext());
lastKnownLocationObservable = locationProvider.getLastKnownLocation();
}
private void requestActivatingGPS() {
LocationRequest locationRequest = LocationRequest.create()
.setPriority(LocationRequest.PRIORITY_HIGH_ACCURACY)
.setNumUpdates(5)
.setInterval(100);
locationUpdatesObservable = locationProvider.getUpdatedLocation(locationRequest);
LocationSettingsRequest.Builder builder = new LocationSettingsRequest.Builder()
.addLocationRequest(locationRequest).setAlwaysShow(true);
PendingResult<LocationSettingsResult> result =
LocationServices.SettingsApi.checkLocationSettings(mGoogleApiClient, builder.build());
result.setResultCallback(new ResultCallback<LocationSettingsResult>() {
@Override
public void onResult(@NonNull LocationSettingsResult locationSettingsResult) {
final Status status = locationSettingsResult.getStatus();
switch (status.getStatusCode()) {
case LocationSettingsStatusCodes.SUCCESS:
getLastKnowLocation();
break;
case LocationSettingsStatusCodes.RESOLUTION_REQUIRED:
try {
// Show the dialog by calling startResolutionForResult(),
// and check the result in onActivityResult().
status.startResolutionForResult(MapsActivity.this,
REQUEST_CHECK_SETTINGS);
} catch (IntentSender.SendIntentException e) {
e.printStackTrace();
}
break;
case LocationSettingsStatusCodes.SETTINGS_CHANGE_UNAVAILABLE:
Log.e(TAG, "Error happen during show Dialog for Turn of GPS");
break;
}
}
});
}
@Override
public void onMapReady(GoogleMap googleMap) {
mMap = googleMap;
LatLng center = new LatLng(drawingOption.getLocationLatitude(), drawingOption.getLocationLongitude());
mMap.moveCamera(CameraUpdateFactory.newLatLngZoom(center, drawingOption.getZoom()));
mMap.setOnMapClickListener(new GoogleMap.OnMapClickListener() {
@Override
public void onMapClick(LatLng latLng) {
@IdRes int icon = R.drawable.ic_add_location_light_green_500_36dp;
BitmapDescriptor bitmap = BitmapDescriptorFactory.fromBitmap(getBitmapFromDrawable(MapsActivity.this, icon));
Marker marker = mMap.addMarker(new MarkerOptions().position(latLng).icon(bitmap).draggable(true));
marker.setTag(latLng);
markerList.add(marker);
points.add(latLng);
if (drawingOption.getDrawingType() == DrawingOption.DrawingType.POLYGON) {
drawPolygon(points);
setAreaLength(points);
} else if (drawingOption.getDrawingType() == DrawingOption.DrawingType.POLYLINE) {
drawPolyline(points);
setLength(points);
}
}
});
mMap.setOnMarkerDragListener(new GoogleMap.OnMarkerDragListener() {
@Override
public void onMarkerDragStart(Marker marker) {
}
@Override
public void onMarkerDrag(Marker marker) {
updateMarkerLocation(marker, false);
}
@Override
public void onMarkerDragEnd(Marker marker) {
updateMarkerLocation(marker, true);
}
});
}
private void updateMarkerLocation(Marker marker, boolean calculate) {
LatLng latLng = (LatLng) marker.getTag();
int position = points.indexOf(latLng);
points.set(position, marker.getPosition());
marker.setTag(marker.getPosition());
if (drawingOption.getDrawingType() == DrawingOption.DrawingType.POLYGON) {
drawPolygon(points);
if (calculate)
setAreaLength(points);
} else if (drawingOption.getDrawingType() == DrawingOption.DrawingType.POLYLINE) {
drawPolyline(points);
if (calculate)
setLength(points);
}
}
private void drawPolyline(List<LatLng> latLngList) {
if (polyline != null) {
polyline.remove();
}
PolylineOptions polylineOptions = new PolylineOptions();
polylineOptions.color(drawingOption.getStrokeColor());
polylineOptions.width(drawingOption.getStrokeWidth());
polylineOptions.addAll(latLngList);
polyline = mMap.addPolyline(polylineOptions);
}
private void drawPolygon(List<LatLng> latLngList) {
if (polygon != null) {
polygon.remove();
}
PolygonOptions polygonOptions = new PolygonOptions();
polygonOptions.fillColor(drawingOption.getFillColor());
polygonOptions.strokeColor(drawingOption.getStrokeColor());
polygonOptions.strokeWidth(drawingOption.getStrokeWidth());
polygonOptions.addAll(latLngList);
polygon = mMap.addPolygon(polygonOptions);
}
@Override
protected void onLocationPermissionGranted() {
getLastKnowLocation();
updateLocation();
}
private void updateLocation() {
if (locationUpdatesObservable != null && compositeSubscription != null) {
updatableLocationSubscription = locationUpdatesObservable
.subscribe(new Action1<Location>() {
@Override
public void call(Location location) {
if (currentLocation == null)
moveMapToCenter(location);
currentLocation = location;
moveMarkerCurrentPosition(location);
}
}, new ErrorHandler());
compositeSubscription.add(updatableLocationSubscription);
}
}
private void getLastKnowLocation() {
if (lastKnownLocationObservable != null && compositeSubscription != null) {
lastKnownLocationSubscription =
lastKnownLocationObservable
.subscribe(new Action1<Location>() {
@Override
public void call(Location location) {
currentLocation = location;
moveMapToCenter(location);
}
}, new ErrorHandler());
compositeSubscription.add(lastKnownLocationSubscription);
}
}
private class ErrorHandler implements Action1<Throwable> {
@Override
public void call(Throwable throwable) {
Toast.makeText(MapsActivity.this, "Error occurred.", Toast.LENGTH_SHORT).show();
Log.d(TAG, "Error occurred", throwable);
}
}
public void moveMapToCenter(Location location) {
LatLng latLng = new LatLng(location.getLatitude(), location.getLongitude());
if (mMap != null) {
myLocationMarker(latLng);
mMap.moveCamera(CameraUpdateFactory.newLatLng(latLng));
}
}
public void moveMarkerCurrentPosition(Location location) {
LatLng latLng = new LatLng(location.getLatitude(), location.getLongitude());
if (mMap != null) {
myLocationMarker(latLng);
}
}
private void myLocationMarker(LatLng latLng) {
if (currentMarker != null) {
currentMarker.setPosition(latLng);
} else {
@IdRes int icon = R.drawable.ic_navigation_red_a400_36dp;
BitmapDescriptor bitmap = BitmapDescriptorFactory.fromBitmap(getBitmapFromDrawable(MapsActivity.this, icon));
currentMarker = mMap.addMarker(new MarkerOptions()
.position(latLng)
.icon(bitmap)
.draggable(false));
}
}
@Override
protected void onStop() {
super.onStop();
if (lastKnownLocationSubscription != null && updatableLocationSubscription != null && compositeSubscription != null) {
compositeSubscription.unsubscribe();
compositeSubscription.clear();
updatableLocationSubscription.unsubscribe();
lastKnownLocationSubscription.unsubscribe();
}
}
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
switch (requestCode) {
case REQUEST_CHECK_SETTINGS:
switch (resultCode) {
case RESULT_OK:
// All required changes were successfully made
Log.d(TAG, "User enabled location");
getLastKnowLocation();
updateLocation();
isGPSOn = true;
break;
case RESULT_CANCELED:
// The user was asked to change settings, but chose not to
Log.d(TAG, "User Cancelled enabling location");
isGPSOn = false;
break;
default:
break;
}
break;
}
}
@Override
public void onConnected(@Nullable Bundle bundle) {
}
@Override
public void onConnectionSuspended(int i) {
}
@Override
public void onConnectionFailed(@NonNull ConnectionResult connectionResult) {
}
private void returnCurrentPosition() {
if (points.size() > 0) {
Intent returnIntent = new Intent();
LatLng[] latLngs = new LatLng[points.size()];
points.toArray(latLngs);
DataModel dataModel = new DataModel();
dataModel.setCount(points.size());
dataModel.setPoints(latLngs);
returnIntent.putExtra(POINTS, dataModel);
setResult(RESULT_OK, returnIntent);
} else {
setResult(RESULT_CANCELED);
}
finish();
}
private static Bitmap getBitmapFromDrawable(Context context, int icon) {
Drawable drawable = ContextCompat.getDrawable(context, icon);
Bitmap obm = Bitmap.createBitmap(drawable.getIntrinsicWidth(), drawable.getIntrinsicHeight(), Bitmap.Config.ARGB_8888);
Canvas canvas = new Canvas(obm);
drawable.setBounds(0, 0, canvas.getWidth(), canvas.getHeight());
drawable.draw(canvas);
return obm;
}
private void setAreaLength(List<LatLng> points) {
areaTextView.setText(getString(R.string.area_label) + String.format(Locale.ENGLISH, "%.2f", CalUtils.getArea(points)) + getString(R.string.mm_label));
lengthTextView.setText(getString(R.string.length_label) + String.format(Locale.ENGLISH, "%.2f", CalUtils.getLength(points)) + getString(R.string.m_label));
}
private void setLength(List<LatLng> points) {
lengthTextView.setText(getString(R.string.length_label) + String.format(Locale.ENGLISH, "%.2f", CalUtils.getLength(points)) + getString(R.string.m_label));
}
}